#ifndef __CVECTOR3D_H__
#define __CVECTOR3D_H__

#include "StdDefs.h"
#include "CMathFixed.h"
 


// CVector3d* must maintain casting ability with (fixed*).
// CVector3d may not have virtuals, inherited, or instantiated data.


class CVector3d
{
public:
	CVector3d(){}
	CVector3d( fixed ijDegrees, fixed ikDegrees );
	CVector3d( const fixed* ijk );
	CVector3d( fixed i, fixed j, fixed k );
	CVector3d( const CVector3d& v );
	~CVector3d(){}

	fixed				Length() const;

	CVector3d&			Clear();
	CVector3d&			Set( fixed i, fixed j, fixed k );
	CVector3d&			Set( const CVector3d& v );
	CVector3d&			Normalize();
	CVector3d&			Cross( const CVector3d& v );
	CVector3d&			ProjOnto( const CVector3d& v );
	CVector3d&			LerpTo( const CVector3d& v, fixed u ); 
	CVector3d&			RotateX( fixed degrees );
	CVector3d&			RotateY( fixed degrees );
	CVector3d&			RotateZ( fixed degrees );

	fixed				Angle( CVector3d& v ) const;
	fixed				AngleTo( CVector3d& v ) const;

	fixed				ShortestDistanceToLineSegment( class CLineSegment3d& segment, 
													   CVector3d& pntOfIntersection ) const;
	fixed				ShortestDistanceToLineSegment( const CVector3d& segV0, const CVector3d& segV1,
													   CVector3d& pntOfIntersection ) const;

	fixed				IsOnLineSegment( class CLineSegment3d& segment ) const;
	fixed				IsOnLineSegment( const CVector3d& segV0, const CVector3d& segV1 ) const;

	fixed				Max() const;

	CVector3d&			operator=( fixed degrees );
	CVector3d&			operator=( const fixed* ijk );

	CVector3d&			operator+=( const CVector3d& v );
	CVector3d&			operator+=( fixed val );
	CVector3d&			operator-=( const CVector3d& v );
	CVector3d&			operator-=( fixed val );
	CVector3d&			operator*=( fixed val );
	CVector3d&			operator*=( const class CMatrix3d& a );
	CVector3d&			operator*=( const class CMatrix4dh& a );
	CVector3d&			operator*=( const class CMatrix4d& a );
	CVector3d&			operator/=( fixed val );
	
	CVector3d&			operator-();

	fixed&				operator[]( int idx );
	fixed const&		operator[]( int idx ) const;

	__inline operator	fixed const*() const { return &m_i; }
	__inline operator	fixed*() { return &m_i; }

	friend CVector3d	operator+( const CVector3d& v1, const CVector3d& v2 );
	friend CVector3d	operator+( const CVector3d& v, fixed val );
	friend CVector3d	operator+( fixed val, const CVector3d& v );
	friend CVector3d	operator-( const CVector3d& v1, const CVector3d& v2 );
	friend CVector3d	operator-( const CVector3d& v, fixed val );
	friend CVector3d	operator-( fixed val, const CVector3d& v );
	friend fixed		operator*( const CVector3d& v1, const CVector3d& v2 );
	friend CVector3d	operator*( const CVector3d& v, fixed val );
	friend CVector3d	operator*( fixed val, const CVector3d& v );
	friend CVector3d	operator*( const class CMatrix3d& a, const CVector3d& x );
	friend CVector3d	operator*( const class CMatrix4dh& a, const CVector3d& x );
	friend CVector3d	operator*( const class CMatrix4d& m, const CVector3d& v );
	friend CVector3d	operator/( const CVector3d& v, fixed val );
	friend CVector3d	operator/( fixed val, const CVector3d& v );
	
	fixed				m_i; 
	fixed				m_j;
	fixed				m_k;
};


 
 

inline 
CVector3d::CVector3d( fixed i, fixed j, fixed k )
{ 
	m_i = i; 
	m_j = j; 
	m_k = k; 
}


inline 
CVector3d::CVector3d( const CVector3d& v )
{
	m_i = v.m_i; 
	m_j = v.m_j; 
	m_k = v.m_k; 
}


inline
fixed CVector3d::Length() const
{
	return CMathFixed::Sqrt( ( *this * *this ) );
}


inline
CVector3d& CVector3d::Clear()
{
	m_i = 0; 
	m_j = 0;
	m_k = 0;

	return *this;
}


inline
CVector3d& CVector3d::Set( fixed i, fixed j, fixed k )
{
	m_i = i; 
	m_j = j;
	m_k = k;

	return *this;
}


inline 
CVector3d& CVector3d::Set( const CVector3d& v )
{
	m_i = v.m_i; 
	m_j = v.m_j; 
	m_k = v.m_k; 

	return *this;
}


inline
CVector3d& CVector3d::Normalize()
{
	fixed len = Length();

	if( len )
		*this /= len;

	return *this;
}


inline
CVector3d& CVector3d::operator-() {
	m_i = -m_i;
	m_j = -m_j;
	m_k = -m_k;
	return *this;
}


inline
CVector3d& CVector3d::operator=( fixed degrees )
{
	ASSERT( FALSE ); // Not yet implemented

	return *this;
}


inline
CVector3d& CVector3d::operator=( const fixed* ijk )
{
	ASSERT( ijk );

	m_i = *ijk;
	m_j = *( ijk + 1 );
	m_k = *( ijk + 2 );

	return *this;
}
	

inline
CVector3d& CVector3d::operator+=( const CVector3d& v )
{
	m_i += v.m_i;
	m_j += v.m_j;
	m_k += v.m_k;

	return *this;
}


inline
CVector3d& CVector3d::operator+=( fixed val )
{
	m_i += val;
	m_j += val;
	m_k += val;

	return *this;
}


inline
CVector3d& CVector3d::operator-=( const CVector3d& v )
{
	m_i -= v.m_i;
	m_j -= v.m_j;
	m_k -= v.m_k;

	return *this;
}


inline
CVector3d& CVector3d::operator-=( fixed val )
{
	m_i -= val;
	m_j -= val;
	m_k -= val;

	return *this;
}


inline
CVector3d& CVector3d::operator*=( fixed val )
{
	m_i = CMathFixed::Mul( m_i, val );
	m_j = CMathFixed::Mul( m_j, val );
	m_k = CMathFixed::Mul( m_k, val );

	return *this;
}


inline
CVector3d& CVector3d::operator/=( fixed val )
{
	m_i = CMathFixed::Div( m_i, val );
	m_j = CMathFixed::Div( m_j, val );
	m_k = CMathFixed::Div( m_k, val );

	return *this;
}


inline
fixed& CVector3d::operator[]( int idx )
{
	ASSERT( idx < 3 );

	return *( ( (fixed*)this ) + idx );
}


inline
fixed const& CVector3d::operator[]( int idx ) const
{
	ASSERT( idx < 3 );

	return *( ( (fixed*)this ) + idx );
}


inline 
CVector3d operator+( const CVector3d& v1, const CVector3d& v2 )
{ 
	return CVector3d( v1.m_i + v2.m_i, v1.m_j + v2.m_j, v1.m_k + v2.m_k ); 
}


inline 
CVector3d operator+( const CVector3d& v, fixed val )
{ 
	return CVector3d( v.m_i + val, v.m_j + val, v.m_k + val ); 
}


inline 
CVector3d operator+( fixed val, const CVector3d& v )
{ 
	return CVector3d( val + v.m_i, val + v.m_j, val + v.m_k ); 
}


inline 
CVector3d operator-( const CVector3d& v1, const CVector3d& v2 )
{ 
	return CVector3d( v1.m_i - v2.m_i,  v1.m_j - v2.m_j, v1.m_k - v2.m_k ); 
}


inline 
CVector3d operator-( const CVector3d& v, fixed val )
{ 
	return CVector3d( v.m_i - val, v.m_j - val, v.m_k - val ); 
}


inline 
CVector3d operator-( fixed val, const CVector3d& v )
{ 
	return CVector3d( val - v.m_i, val - v.m_j, val - v.m_k ); 
}


inline
fixed operator*( const CVector3d& v1, const CVector3d& v2 )
{
	return CMathFixed::Mul( v1.m_i, v2.m_i ) + CMathFixed::Mul( v1.m_j, v2.m_j ) + 
		   CMathFixed::Mul( v1.m_k, v2.m_k );
}


inline
CVector3d operator*( const CVector3d& v, fixed val )
{
	return CVector3d( CMathFixed::Mul( v.m_i, val ), CMathFixed::Mul( v.m_j, val ),
					  CMathFixed::Mul( v.m_k, val ) );
}


inline
CVector3d operator*( fixed val, const CVector3d& v )
{
	return CVector3d( CMathFixed::Mul( val, v.m_i ), CMathFixed::Mul( val, v.m_j ),
					  CMathFixed::Mul( val, v.m_k ) );
}

inline
CVector3d operator*( const class CMatrix4d& m, const CVector3d& v )
{
	CVector3d p(v);

	return p *= m;
}

inline
CVector3d operator/( const CVector3d& v, fixed val )
{
	return CVector3d( CMathFixed::Div( v.m_i, val ), CMathFixed::Div( v.m_j, val ),
					  CMathFixed::Div( v.m_k, val ) );
}


inline
CVector3d operator/( fixed val, const CVector3d& v )
{
	return CVector3d( CMathFixed::Div( val, v.m_i ), CMathFixed::Div( val, v.m_j ),
					  CMathFixed::Div( val, v.m_k ) );
}


 

#endif // __CVECTOR3D_H__